package com.jweb.framework.security.data.transform;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ResourceBundle;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;

import com.jweb.framework.security.pass.Global;
import com.jweb.framework.security.pass.IEncryptPass;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：数据库部分字段加密密码生成(出于安全方面的考虑，此代码无需打成JAR包。)
 */
public class PwdEncryptPass implements IEncryptPass {
	
	/**
	 * Cipher：此类为加密和解密提供密码功能。
	 * 		为创建Cipher对象，应用程序调用Cipher的getInstance方法并将所请求转换的名称传递给它。
	 */
	private Cipher encip1 ;
	private Cipher decip1 ;
	private Cipher encip ;
	private Cipher decip ;
	private String desKey ;
	private String dateKey ;
	
	private final String Algorithm = "DESede" ;
	final byte initKeyBytes[] = { 113, 52, 52, 120, 9, -85, -51, -117, -2, -36,
			-70, -112, -121, 101, 111, 33, -17, -51, -85, 9, 120, 86, 52, 18 };
	
	public PwdEncryptPass(){
		init3DES();
	}
	
	public String encryptStr(String in) {
		try {
			/**
			 * doFinal(byte[] input)：按单部分操作加密或解密数据，或者结束一个多部分操作。数据将被加密或解密（具体取决于Cipher的初始化方式）。
			 * 处理input缓冲区中的字节以及在上一次update操作中缓存的任何输入字节，其中应用了填充（如果请求）。结果将存储在新缓冲区中。
			 * 结束时，此方法将此Cipher对象重置为上一次调用init初始化得到的状态。即该对象被重置，并可用于加密或解密（具体取决于调用init时指定的操作模式）更多的数据。
			 */
			return bcd2asc(this.encip.doFinal(in.getBytes()));
		} catch (Exception e) {
			e.printStackTrace() ;
		}
		
		return null ;
	}
	
	public void createPassFile(String passwd) throws IOException {
		try {
			String fileName = getFileName();
			File file = new File(fileName);
			if(!file.exists()){
				file.createNewFile();
			}
			String passwdEn = encryptStr(passwd);
			String dateMd5 = encrypt1Str(this.dateKey);
			String fileContent = (new StringBuilder(dateMd5)).append(":").append(passwdEn).toString();
			String fileContentMd5 = md5Buffer(fileContent);
			fileContent = (new StringBuilder(String.valueOf(fileContent))).append(":").append(fileContentMd5).toString(); ;
			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file , false));
			bufferedWriter.write(fileContent);
			bufferedWriter.flush() ;
			bufferedWriter.close() ;
			
		} catch (IOException e) {
			e.printStackTrace() ;
			throw e ;
		}
	}
	
	private String encrypt1Str(String in) {
		try {
			return bcd2asc(this.encip1.doFinal(in.getBytes()));
		} catch (Exception e) {
			e.printStackTrace() ;
		}
		
		return null ;
	}
	
	private String getFileName(){
		ResourceBundle bundle = ResourceBundle.getBundle("properties/datapwd");
		String passFile = bundle.getString("passfile");
		return passFile ;
	}
	
	private boolean init3DES() {
		Global.desKey = "CAB774DCC2FE66612919C274B11GG4D456E6DDC10F6D7E64DC0DB194A3EC6261";
		if(Global.desKey != null){
			this.desKey = Global.desKey ;
		}
		try {
			/**
			 * SecretKeyFactory:此类表示秘密密钥的工厂
			 * 密钥工厂用来将密钥（类型key的不透明加密密钥）转换为密钥规范（底层密钥材料的透明表示形式），反之亦然。秘密密钥工厂只对秘密（对称）密钥进行操作。
			 * 密钥工厂为双工模式，即其允许根据给定密钥规范（密钥材料）构建不透明密钥对象，或以适当格式获取密钥对象的底层密钥材料。
			 */
			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(this.Algorithm);
			/**
			 * DESedeKeySpec：此类指定一个DES-EDE("triple-DES")密钥。
			 * DESedeKeySpece(byte[] key):创建一个DESedeKeySpec对象，使用key中的前24个字节作为DES-EDE密钥的密钥内容。
			 * 		组成DES-EDE密钥的字节在key[0]和key[23]范围内（包含两者）。
			 */
			DESedeKeySpec desKeySpec1 = new DESedeKeySpec(this.initKeyBytes);
			/**
			 * SecretKey:秘密（对称）密钥。
			 * 此接口不包含方法或常量。其唯一目的是分组秘密密钥（并为其提供类型安全）。
			 */
			javax.crypto.SecretKey desKey1 = keyFactory.generateSecret(desKeySpec1);
			this.encip1 = Cipher.getInstance(this.Algorithm);
			this.decip1 = Cipher.getInstance(this.Algorithm);
			this.encip1.init(1, desKey1);
			this.decip1.init(2, desKey1);
			
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
			this.dateKey = dateFormat.format(new Date());
			this.desKey = md5Buffer(this.dateKey);
			
			/**
			 * DESedeKeySpece(byte[] key):
			 * 		创建一个DESedekeySpec对象，使用key中的前24个字节作为 DES-EDE密钥的密钥内容。
			 */
			DESedeKeySpec desKeySpec2 = new DESedeKeySpec(this.desKey.getBytes());
			/**
			 * generateSecret(KeySpec keyspec)：根据提供的密钥规范（密钥材料）生成SecretKey对象。
			 * 
			 * SecretKey：秘密（对称）密钥。
			 * 		此接口不包含方法或常量。其唯一目的是分组秘密密钥（并为其提供类型安全）。
			 */
			javax.crypto.SecretKey desKey2 = keyFactory.generateSecret(desKeySpec2);
			/**
			 * Cipher:此类为加密和解密提供密码功能。
			 * 		为创建Cipher对象，应用程序调用Cipher的getInstance方法并将所请求转换的名称传递给它。
			 */
			this.encip = Cipher.getInstance(this.Algorithm);
			this.decip = Cipher.getInstance(this.Algorithm);
			/**
			 * init(int opmode, Key key)：用密钥初始化此Cipher.
			 * 		为以下4种操作之一初始化该Cipher:加密、解密、密钥包装或密钥解包，具体取决于opmode的值。
			 * 注意，初始化Cipher对象时，它将失去所有以前获得的状态。换句话说，初始化Cipher相当于创建该Cipher的一个新实例并将其初始化。
			 */
			this.encip.init(1, desKey2);
			this.decip.init(2, desKey2);
			
		} catch (Exception e) {
			e.printStackTrace() ;
			return false ;
		}
		
		return true ;
	}
	
	private String md5Buffer(String strSrc) {
		try {
			/**
			 * MessageDigest：此MessageDigest类为应用程序提供信息摘要算法的功能，如MD5或SHA算法 。信息摘要是安全的单向哈希函数，它接收任意大小的数据，并输出固定长度的哈希值。
			 * MessageDigest对象开始被初始化。该对象通过使用update方法处理数据。任何时候都可以调用reset方法重置摘要。一旦所有需要更新的数据
			 * 		都已经被更新了，应该调用digest方法之一完成哈希计算。对于给定数量的更新数据，digest方法只能被调用一次。在调用digest之后，
			 * 		MessageDigest对象被重新设置成其初始状态。
			 * 
			 * getInstnace(String algorithm):返回实现指定摘要算法的MessageDigest对象。
			 */
			MessageDigest md = MessageDigest.getInstance("MD5");
			/**
			 * update(byte[] input)：使用指定的byte数组更新摘要 。
			 */
			md.update(strSrc.getBytes());
			/**
			 * digest():通过执行诸如填充之类的最终操作完成哈希计算。在调用此方法之后，摘要被重置。
			 */
			return bcd2asc(md.digest());
		} catch (Exception e) {
			e.printStackTrace() ;
		}
		
		return null ;
	}
	
	private String bcd2asc(byte b[]){
		String hs = "";
		String stmp = "";
		for (int n = 0; n < b.length; n++) {
			/**
			 * toHexString(int i)：以十六进制(基数16)无符号整数形式返回一个整数参数的字符串表示形式。
			 */
			stmp = Integer.toHexString(b[n] & 255);
			if (stmp.length() == 1)
				hs = (new StringBuilder(String.valueOf(hs))).append("0").append(stmp).toString();
			else
				hs = (new StringBuilder(String.valueOf(hs))).append(stmp).toString();
		}

		return hs.toUpperCase();
	}

	@Override
	public void createPassFile(String hostIp, String accName, String passwd)
			throws IOException {
		throw new UnsupportedOperationException() ;
	}

}
